package lex.fractal;

import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.Line2D;

import lex.fractal.algorithm.Fractal;

/**
 * In 1904, Swedish mathematician Helge von Koch presented a paper that included
 * a fractal curve known as the Koch snowflake (also known as the Koch star).<br>
 * The following algorithm recursively generates this fractal, which is based on
 * an equilateral triangle:<br>
 * 1.For each of the equilateral triangle's line segments, divide the line
 * segment into three equal-length line segments.<br>
 * 2.For each middle line segment, generate a smaller equilateral triangle whose
 * base line segment replaces the middle line segment. <br>
 * 3.Remove the base line segment from the previous step's equilateral triangle.
 * Repeat steps 1 through 3 with this new equilateral triangle.
 */
public class SnowflakeFractal implements Fractal
{
	private final static double SIN60 = Math.sin( Math.PI / 3.0 );
	private final static int MAX_DEPTH = 10;
	private final static int OFFSET = 18;
	private Line2D line = new Line2D.Double( 0.0, 0.0, 0.0, 0.0 );
	private Stroke stroke = new BasicStroke( 2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND );

	@Override
	public String getName()
	{
		return "Snowflake";
	}

	public void generate( Graphics2D g, int depth, int width, int height )
	{
		// Precalculate height/5, width/2, and width/5 for speed.
		double h5 = height / 5.0;
		double w2 = width / 2.0;
		double w5 = width / 5.0;
		ks( w2, h5 - OFFSET, w5, 4.0 * h5 - OFFSET, depth, g ); // left side
		ks( w5, 4.0 * h5 - OFFSET, 4.0 * w5, 4.0 * h5 - OFFSET, depth, g ); // bottom
		// side
		ks( 4.0 * w5, 4.0 * h5 - OFFSET, w2, h5 - OFFSET, depth, g ); // right
		// side
	}

	public int getMaximalDepth()
	{
		return MAX_DEPTH;
	}

	private void ks( double x1, double y1, double x2, double y2, int depth, Graphics2D g )
	{
		if( depth <= 0 )
		{
			g.setStroke( stroke );
			line.setLine( x1, y1, x2, y2 );
			g.draw( line );
		}
		else
		{
			double x4 = x1 * 2.0 / 3.0 + x2 / 3.0;
			double y4 = y1 * 2.0 / 3.0 + y2 / 3.0;
			double x5 = x1 / 3.0 + x2 * 2.0 / 3.0;
			double y5 = y1 / 3.0 + y2 * 2.0 / 3.0;
			double x6 = (x4 + x5) / 2.0 + (y4 - y5) * SIN60;
			double y6 = (y4 + y5) / 2.0 + (x5 - x4) * SIN60;
			ks( x1, y1, x4, y4, depth - 1, g );
			ks( x4, y4, x6, y6, depth - 1, g );
			ks( x6, y6, x5, y5, depth - 1, g );
			ks( x5, y5, x2, y2, depth - 1, g );
		}
	}
}
